// shockball.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <math.h>
#include <windows.h>		// Header File For Windows
#include <gl\gl.h>			// Header File For The OpenGL32 Library
#include <gl\glu.h>			// Header File For The GLu32 Library
//#include <gl\glaux.h>		// Header File For The Glaux Library
#include <string>
#include <sstream>
#include <time.h>

#include "glut.h"
#include "game.h"

#define STEP 10
Game game;

float mouse[] = {0,0};
#define FOV 10
#define FOV_OFFSET 0.25
float eye_dist;
int width,height;

void mouseMoved(int x, int y)
{
	mouse[0]=x;
	mouse[1]=y;
}

float get_theta()
{
	float center[2];
	center[0] = ((double)WIDTH)/2.0;
	center[1] = ((double)HEIGHT)/2.0;

	return mouse[game.axis] - center[game.axis];
}

void timeStep(int null)
{
	game.update_pos();
	//schedules the next timestep (done first to keep timing as constant as possible)
	if (game.playing)
		glutTimerFunc(STEP, timeStep, 0);	
	//aplies physics to the ball
	game.handle_gravity(get_theta());
	//detects colisions, corrects position and changes velocity
	game.handle_collisions();
	//detects collisions with boundaries, and corrects accordingly
	game.handle_boundaries();
	//if the mode is outblast, the grid auto-populates
	if (game.mode == OUTBLAST)
		game.rand_populate_grid();
	//refreshes the display with the new vales
	glutPostRedisplay();
}

void setupView(int w, int h)
{
	glViewport(0.0f, 0.0f, (float)(w),float(h));          /* Establish viewing area to cover entire window. */
	glMatrixMode(GL_PROJECTION);     /* Start modifying the projection matrix. */
	glLoadIdentity();                /* Reset projection matrix. */
	gluPerspective((float)(FOV + FOV_OFFSET),(float)(w)/(float)(h),(float)(eye_dist/2),(float)(eye_dist*2));
}

static void initMaterial(void)
{
	/* define material properties */
	GLfloat material_ambient[] = {0.25f, 0.25f, 0.25f};
	GLfloat material_diffuse[] = {0.90f, 0.90f, 0.90f};
	GLfloat material_specular[] = {0.90f, 0.90f, 0.90f};
	GLfloat material_shininess = 25.0f;

	glEnable(GL_DEPTH_TEST);

	/* load material properties */
	glMaterialfv(GL_FRONT, GL_AMBIENT, material_ambient);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, material_diffuse);
	glMaterialfv(GL_FRONT, GL_SPECULAR, material_specular);
	glMaterialf(GL_FRONT, GL_SHININESS, material_shininess);
}

typedef struct
{
	float pos[4];
	float diffuse[4];
	float specular[4];
	float ambient[4];
}light_t;

light_t light={
	{6.0f,10.0f,15.0f,1.0f},//position (the final 1 means the light is positional)
	{1.0f,1.0f,1.0f,1.0f},//diffuse
	{0.0f,0.0f,0.0f,1.0f},//specular
	{0.1f,0.1f,0.1f,1.0f}//ambient
};

static void initLights(void)
{
	glEnable(GL_LIGHTING); //enables lighting
	glEnable(GL_LIGHT0); //enables a light
	glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,0); //sets lighting to one-sided
	glEnable(GL_COLOR_MATERIAL); //causes lighting model to take glColor() calls

	glLightfv(GL_LIGHT0,GL_POSITION,light.pos);			//updates the light's position
	glLightfv(GL_LIGHT0,GL_DIFFUSE,light.diffuse);		//updates the light's diffuse colour
	glLightfv(GL_LIGHT0,GL_SPECULAR,light.specular);	//updates the light's specular colour
	glLightfv(GL_LIGHT0,GL_AMBIENT,light.ambient);		//updates the light's ambient colour
	
}

void initGL(void)
{
	/* set the clear colour (R, G, B, A) */
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	/* initialize materials */
	initMaterial();
	/* initialize lights */
	initLights();
	/* turns on line smooting */
	glEnable (GL_LINE_SMOOTH);
	/* enable smooth shading */
	glShadeModel(GL_SMOOTH);
	/* initialize textures */
	game.init_textures();
	/* initialize callList for ball */
	game.init_ball();
	/* initialize callList for block */
	game.init_block();
	/* initialize callList for grid */
	game.init_grid();
	/* start the animation loop */
	glutTimerFunc(STEP,timeStep,0);
}

void paint_score()
{
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix(); glLoadIdentity();
	glMatrixMode(GL_PROJECTION);
	glPushMatrix(); glLoadIdentity();
	{

		glOrtho(0,WIDTH,0,HEIGHT,-1,1);

		glDisable(GL_LIGHTING);
		glColor3f(1.0f,1.0f,1.0f);
		glRasterPos2f(0, HEIGHT-10);
		std::stringstream str;
		str << "Score: " << game.score << " Level: " << game.types - 1 << " Timer: " << HIT_TIMEOUT + 1 - (time(NULL) - game.last_hit);
		for (unsigned int i = 0; i < str.str().length(); i++) {
			glutBitmapCharacter(GLUT_BITMAP_9_BY_15, str.str().c_str()[i]);
		}
		glEnable(GL_LIGHTING);
	}
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
}

void paintGL(void)
{
	/* clears the depth and color buffers */
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	
	/* sets transformation to identity */
    glLoadIdentity();
	/* sets up the perspective */
	setupView(width,height);
	/* sets the eye based on the tilt of the board */
	gluLookAt((float)(width/2 + (width/2 - mouse[0])),(float)(height/2 - (height/2 - mouse[1])),(float)(eye_dist),(float)(width/2),(float)(height/2),0.0f,0.0f,1.0f,0.0f);
	/* paints the background */
	game.paint_backdrop();
	/* set up blending */
	glDisable(GL_DEPTH_TEST); //turn depth testing off
	glEnable(GL_BLEND); // turn blending on
	glBlendFunc(GL_SRC_ALPHA,GL_ONE); //set the blend function
	// Iterates over the grid and draws blocks one at a time 
	for(int i = 0; i < COLS; i++)
	{
		for(int j = 0; j < ROWS; j++)
		{
			Block * block = game.grid[i][j];
			if (block != NULL)
			{			
				/* sets the color for the block */
				game.set_color(block->type, 0.9f);
				/* moves to the appropriate location */
				glTranslatef(block->center[0],block->center[1],0);
				/*binds the appropriate texture to the top face */
				game.set_texture(block->type);
				/* draw the block */
				glCallList(BLOCK);
				/* reset to zero (makes it aesier to draw the next object) */
				glTranslatef(-block->center[0],-block->center[1],0);
			}
		}
	}
	/* re-enable depth testing */
	glDisable(GL_BLEND); // turn blending off
	glEnable(GL_DEPTH_TEST);
	/* paints the grid */
	glCallList(GRID);
	/* moves to the appropriate location */
	glTranslatef(game.center[0],game.center[1],0);
	/* sets the color for the ball */
	game.set_color(game.type, 1.0f);
	/* draw the ball */
	glCallList(BALL);
	/* reset to zero (makes it easier to draw the next object) */
	glTranslatef(-game.center[0],-game.center[1],0);
	/* draws the score */
	paint_score();
	/* double buffered */
	glutSwapBuffers();
}

void resizeGL(int w, int h)
{
	width = w;
	height = h;
	setupView(w,h);  
}

int _tmain(int argc, _TCHAR* argv[])
{
	//calculates that distance from the eye to the board in perspective view
	float PI = 3.14159265;
	eye_dist = (float)HEIGHT / (2 * tan((float)(FOV/2)*PI/180));

	game.init_game(1);
	glutInitDisplayMode( GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA );
	glutInitWindowSize(WIDTH,HEIGHT);
	glutCreateWindow("Balls");
	initGL();
	glutPassiveMotionFunc(mouseMoved);
	glutReshapeFunc(resizeGL);
	glutDisplayFunc(paintGL);  
	glutMainLoop();
	return 0;
	return 0;
}

